import cv2
import numpy
import sys

def nothing():
    pass

def Representationmedium(Input):
        result = int(Input)
        if result > 255:
            result = 255
        elif result < 0:
            result = 0
        else:
            result = int(result)
        return result

def Adjust(InputArray, Ratio, Code=0):
    cols = InputArray.shape[0]
    rows = InputArray.shape[1]
    OutputArray = numpy.zeros(InputArray.shape, dtype=InputArray.dtype)
    HLS = cv2.cvtColor(sourceImage, cv2.COLOR_BGR2HLS)
    HSV = cv2.cvtColor(sourceImage, cv2.COLOR_BGR2HSV)
    for col in range(0, cols):
        for row in range(0, rows):
            if Code == 0 or Code == 1:
                B = float(sourceImage.item(col, row, 0))
                G = float(sourceImage.item(col, row, 1))
                R = float(sourceImage.item(col, row, 2))
                if Code == 0:
                    Channel1 = B * Ratio
                    Channel2 = G * Ratio
                    Channel3 = R * Ratio
                else:
                    Channel1 = B / 255
                    Channel2 = G / 255
                    Channel3 = R / 255
                    Channel1 = ((Channel1 - 0.5) * Ratio + 0.5) * 255
                    Channel2 = ((Channel2 - 0.5) * Ratio + 0.5) * 255
                    Channel3 = ((Channel3 - 0.5) * Ratio + 0.5) * 255
            elif Code == 2:
                Channel1 = HLS.item(col, row, 0)
                Channel2 = HLS.item(col, row, 1)
                Channel3 = HLS.item(col, row, 2)
                Channel3 = Channel3 * Ratio
            elif Code == 3:
                Channel1 = HSV.item(col, row, 0)
                Channel2 = HSV.item(col, row, 1)
                Channel3 = HSV.item(col, row, 2)
                Channel1 = Channel1 + Ratio
            Channel1 = Representationmedium(Channel1)
            Channel2 = Representationmedium(Channel2)
            Channel3 = Representationmedium(Channel3)
            OutputArray.itemset(col, row, 0, Channel1)
            OutputArray.itemset(col, row, 1, Channel2)
            OutputArray.itemset(col, row, 2, Channel3)
    if Code == 2:
        return cv2.cvtColor(OutputArray, cv2.COLOR_HLS2BGR)
    elif Code == 3:
        return cv2.cvtColor(OutputArray, cv2.COLOR_HSV2BGR)
    return OutputArray

def getHistogram(InputArray):
    y = 300
    OutputArray = numpy.zeros((y, 512, 3), numpy.uint8)
    grayImage = cv2.cvtColor(InputArray, cv2.COLOR_BGR2GRAY)
    cols = grayImage.shape[0]
    rows = grayImage.shape[1]
    N = [0 for i in range(0, 256)]
    max = 0
    for col in range(0, cols):
        for row in range(0, rows):
            value = grayImage.item(col, row)
            N[value] += 1
            if N[value] > max:
                max = N[value]
    for i in range(0, 256):
        cv2.line(OutputArray, (i*2, y), (i*2, y - N[i]*300/max), (0, 255, 0), 2)
    return OutputArray

def MedianFilter(InputArray, CoreSize):
    cols = InputArray.shape[0]
    rows = InputArray.shape[1]
    OutputArray = numpy.zeros(InputArray.shape, dtype=InputArray.dtype)
    diffCol = (CoreSize[0] - 1) / 2
    diffRow = (CoreSize[1] - 1) / 2
    for row in range(diffRow, rows - diffRow):
        for col in range(diffCol, cols - diffCol):
            for channel in range(0, 3):
                allNum = []
                for i in range(col - diffCol, col + diffCol + 1):
                    for j in range(row - diffRow, row + diffRow + 1):
                        if i != col and j != row:
                            allNum += [InputArray.item(i, j, channel)]
                numArray = numpy.array(allNum)
                OutputArray.itemset(col, row, channel, numpy.median(numArray))
    return OutputArray

def MeanFilter(InputArray, CoreSize):
    cols = InputArray.shape[0]
    rows = InputArray.shape[1]
    OutputArray = numpy.zeros(InputArray.shape, dtype=InputArray.dtype)
    diffCol = (CoreSize[0] - 1) / 2
    diffRow = (CoreSize[1] - 1) / 2
    for row in range(diffRow, rows - diffRow):
        for col in range(diffCol, cols - diffCol):
            for channel in range(0, 3):
                allNum = []
                for i in range(col - diffCol, col + diffCol + 1):
                    for j in range(row - diffRow, row + diffRow + 1):
                        if i != col and j != row:
                            allNum += [InputArray.item(i, j, channel)]
                numArray = numpy.array(allNum)
                OutputArray.itemset(col, row, channel, numpy.mean(numArray))
    return OutputArray

def Roberts(InputArray):
    GrayArray = cv2.cvtColor(InputArray, cv2.COLOR_BGR2GRAY)
    cols = GrayArray.shape[0]
    rows = GrayArray.shape[1]
    OutputArray = numpy.zeros(GrayArray.shape, dtype=GrayArray.dtype)
    for col in range(0, cols - 1):
        for row in range(0, rows - 1):
            A0 = GrayArray.item(col  , row  )
            A1 = GrayArray.item(col  , row+1)
            A2 = GrayArray.item(col+1, row  )
            A3 = GrayArray.item(col+1, row+1)
            A = abs(A0 - A3) + abs(A1 - A2)
            OutputArray.itemset(col, row, A)
    return OutputArray

def Sobel(InputArray):
    GrayArray = cv2.cvtColor(InputArray, cv2.COLOR_BGR2GRAY)
    cols = GrayArray.shape[0]
    rows = GrayArray.shape[1]
    OutputArray = numpy.zeros(GrayArray.shape, dtype=GrayArray.dtype)
    for col in range(0, cols - 1):
        for row in range(0, rows - 1):
            A0 = GrayArray.item(col-1, row-1)
            A1 = GrayArray.item(col  , row-1)
            A2 = GrayArray.item(col+1, row-1)
            A3 = GrayArray.item(col+1, row  )
            A4 = GrayArray.item(col+1, row+1)
            A5 = GrayArray.item(col  , row+1)
            A6 = GrayArray.item(col-1, row+1)
            A7 = GrayArray.item(col-1, row  )
            A = abs(A0 + 2*A1 + A2 - A6 - 2*A5 - A4) + abs(A0 + 2*A7 + A6 - A2 - 2*A3 - A4)
            OutputArray.itemset(col, row, A)
    return OutputArray

def QuickMedianFilter(InputArray, CoreSize):
    cols = InputArray.shape[0]
    rows = InputArray.shape[1]
    OutputArray = numpy.zeros(InputArray.shape, dtype=InputArray.dtype)
    if CoreSize[0] % 2 != 1 or CoreSize[1] % 2 != 1:
        print 'Core size is not odd.'
        return -1
    th = (CoreSize[0] * CoreSize[1] - 1) / 2
    diffCol = (CoreSize[0] - 1) / 2
    diffRow = (CoreSize[1] - 1) / 2
    for channel in range(0, 3):
        for row in range(diffRow, rows - diffRow):
            hist = [0 for k in range(0, 256)]
            allNum = []
            for m in range(0, CoreSize[0]):
                for n in range(row - diffRow, row - diffRow + CoreSize[1]):
                    if m != diffCol and n != row:
                        value = InputArray.item(m, n, channel)
                        allNum += [value]
                        temp = hist[value]
                        hist[value] = temp + 1
            numArray = numpy.array(allNum)
            median = int(numpy.median(numArray))
            OutputArray.itemset(diffCol, row, channel, median)
            Ltmdn = 0
            for h in range(0, 256):
                if h < median:
                    temp = Ltmdn
                    Ltmdn = temp + hist[h]
            for col in range(diffCol+1, cols - diffCol):
                L_X = col - diffCol - 1
                for y in range(row - diffRow, row - diffRow + CoreSize[1]):
                    L = InputArray.item(L_X, y, channel)
                    temp = hist[L]
                    hist[L] = temp - 1
                    if L < median:
                        temp = Ltmdn
                        Ltmdn = temp - 1
                R_X = col + diffCol
                for y in range(row - diffRow, row - diffRow + CoreSize[1]):
                    R = InputArray.item(R_X, y, channel)
                    temp = hist[R]
                    hist[R] = temp + 1
                    if R < median:
                        temp = Ltmdn
                        Ltmdn = temp + 1
                record = median
                while Ltmdn > th:
                    if median == 0:
                        median = record
                        break
                    temp = median
                    median = temp - 1
                    temp = Ltmdn
                    Ltmdn = temp - hist[median]
                while Ltmdn + hist[median] <= th:
                    if median == 255:
                        median = record
                        break
                    temp = Ltmdn
                    Ltmdn = temp + hist[median]
                    temp = median
                    median = temp + 1
                OutputArray.itemset(col, row, channel, median)
    return OutputArray

endMark = cv2.waitKey(1)
filePath = sys.argv[1]
sourceImage = cv2.imread(filePath)
choice = '0'
while choice != '6':
    print 'Choices: '
    print '1.Adjust of the picture.'
    print '2.Show the histogram.'
    print '3.Median filter and mean filter.'
    print '4.Roberts and Sobel'
    print '5.Fast median filter.'
    print '6.Quit'
    choice = raw_input()
    print '---------------Press the cross to close the window.--------'
    print '-------------------Do not press any key.-------------------'
    if choice == '1':
        window = cv2.namedWindow('result')
        valueTrackbar = cv2.createTrackbar('Value', 'result', 50, 100, nothing)
        switchTrackbar = cv2.createTrackbar('0:Brightness\n1:Contrast\n2:Saturation\n3.Hue', 'result', 0, 3, nothing)
        co = True
        while co:
            Ratio = cv2.getTrackbarPos('Value', 'result')
            switch = cv2.getTrackbarPos('0:Brightness\n1:Contrast\n2:Saturation\n3.Hue', 'result')
            if switch != 3:
                Ratio = Ratio / 50.0
            destinyImage = Adjust(sourceImage, Ratio, switch)
            cv2.imshow('result', destinyImage)
            key = cv2.waitKey(1)
            if key != endMark:
                co = False
    elif choice == '2':
        hist = getHistogram(sourceImage)
        cv2.imshow('Histogram', hist)
        cv2.imwrite('Histogram.jpg', hist)
    elif choice == '3':
        print 'Input the size of core in X axis(odd number):'
        x = int(raw_input())
        print 'Input the size of core in Y axis(odd number):'
        y = int(raw_input())
        if x % 2 != 1 or y % 2 != 1:
            print 'Core size is not odd.'
            cv2.imshow('Source Image', sourceImage)
        else:
            median = MedianFilter(sourceImage, (x, y))
            cv2.imshow('Median Filter', median)
            cv2.imwrite('MedianFilter.jpg', median)
            mean = MeanFilter(sourceImage, (x, y))
            cv2.imshow('Mean Filter', mean)
            cv2.imwrite('MeanFilter.jpg', mean)
    elif choice == '4':
        print 'Input the size of core in X axis(odd number):'
        x = int(raw_input())
        print 'Input the size of core in Y axis(odd number):'
        y = int(raw_input())
        if x % 2 != 1 or y % 2 != 1:
            print 'Core size is not odd.'
            cv2.imshow('Source Image', sourceImage)
        else:
            median = MedianFilter(sourceImage, (x, y))
        roberts = Roberts(median)
        cv2.imshow('Roberts', roberts)
        cv2.imwrite('Roberts.jpg', roberts)
        sobel = Sobel(median)
        cv2.imshow('Sobel', sobel)
        cv2.imwrite('Sobel.jpg', sobel)
    elif choice == '5':
        print 'Input the size of core in X axis(odd number):'
        x = int(raw_input())
        print 'Input the size of core in Y axis(odd number):'
        y = int(raw_input())
        if x % 2 != 1 or y % 2 != 1:
            print 'Core size is not odd.'
            cv2.imshow('Source Image', sourceImage)
        else:
            quick = QuickMedianFilter(sourceImage, (x, y))
            cv2.imshow('Fast Median Filter', quick)
            cv2.imwrite('FastMedian.jpg', quick)
    else:
        break
    cv2.waitKey()
